#include "ComponentAntiCheatBase.h"
#include "Components/ComponentBody.h"
#include "Components/ComponentPlayer.h"
#include "Components/ComponentCreature.h"
#include "Components/ComponentClothing.h"
#include "Components/ComponentHealth.h"
#include "Components/ComponentMiner.h"
#include "Player/PlayerData.h"
#include "Log.h"
#include "Network/PackageManager.h"
#include "Network/Packages/ComponentHealthPackage.h"
#include "Subsystems/SubsystemTimeOfDay.h"

#include "Expand/PluginsManager.h"
#include "Expand/Plugins/AuthPlugin.h"

#include "GameSingleton.h"
#include "GameRegistry.hpp"
#include "GameRandom.hpp"

#include "Subsystems/SubsystemTimeOfDay.h"
#include "Subsystems/SubsystemTerrain.h"

#include "Blocks/AirBlock.h"
#include "Network/Client.h"

using namespace PocketSurvival::Engine;

namespace PocketSurvival
{

    void ComponentAntiCheatBase::update(float dt)
    {
        std::shared_ptr<PlayerData> playerData = componentPlayer->playerData;

        // 创造模式或管理员可以跳过检测
        if (playerData->gameMode == GameMode::Creative || authPlugin->getLevelByGuid(playerData->playerGuid) >= 100)
        {
            return;
        }

        ComponentBody *componentBody = componentPlayer->componentBody;
        Vector3 position;
        Vector3 velocity;
        componentBody->getPosition(position);
        componentBody->getVelocity(velocity);

        if (allowAnomaliesTime > 0.0f)
        {
            allowAnomaliesTime = MathUtils::Max<float>(allowAnomaliesTime - dt, 0);
        }
        
        if (playerData->getLoadTerrainCompiletedCount() < 3 || playerData->getInServerTime() < 15.0f || allowAnomaliesTime > 0.0f)
        {
            lastPosition = position;
            lastVelocity = velocity;
            return;
        }

        if (lastVelocity != velocity)
        {
            lastVelocity = velocity;
            lastVelocityY = velocity.posY;
            Vector2 velocity2(velocity.posX, velocity.posZ);
            if (velocity2.length() > 200 * dt)
            {
                if (numberOfAnomalies % 5 == 0)
                {
                    Log::Warn(fmt::format("玩家 {} 的速度异常, {}", playerData->username, velocity2.toString()));
                }
                addCheatCount(3);
            }
        }

        float changeY = 0.0f;

        {
            const Vector3 &lastPos = lastPosition;
            changeY = position.posY - lastPos.posY;

            TerrainChunk *chunkAtCell = terrain->getTerrainChunk(Terrain::ToCell(lastPos.posX) >> 4, Terrain::ToCell(lastPos.posZ) >> 4);
            if (chunkAtCell == nullptr || chunkAtCell->State <= TerrainChunkState::InvalidContents4)
            {
                goto END_CHECK;
            }

            if (position != lastPos)
            {
                Vector2 positionXYChange(position.posX - lastPos.posX, position.posZ - lastPos.posZ);

                bool needUpdateLastPosition = true;

                if (positionXYChange.length() >= 60.0f * dt)
                {
                    if (numberOfAnomalies % 5 == 0)
                    {
                        Log::Warn(fmt::format("玩家 {} 的位置变换异常, {} => {}", playerData->username, lastPos.toString(), position.toString()));
                    }
                    addCheatCount(1);
                    componentPlayer->setNetPosition(lastPos);
                    needUpdateLastPosition = false;
                }
                else if (positionXYChange.length() >= 40.0f * dt && componentPlayer->componentHealth->health == 0.0f)
                {
                    if (numberOfAnomalies >= 10)
                    {
                        Log::Warn(fmt::format("玩家 {} 没血了，但是变换了位置, {} => {}", playerData->username, lastPos.toString(), position.toString()));
                    }
                    addCheatCount(1);
                }

                if (MathUtils::Abs<float>(changeY) > 200.0f * dt)
                {
                    if (numberOfAnomalies % 5 == 0)
                    {
                        Log::Warn(fmt::format("玩家 {} 的位置高度变换异常, {} => {}", playerData->username, lastPos.toString(), position.toString()));
                    }
                    addCheatCount(2);
                    componentPlayer->setNetPosition(lastPos);
                    needUpdateLastPosition = false;
                }

                if (needUpdateLastPosition)
                {
                    lastPosition = position;
                }
            }
        }

        if (timeOfDay->periodicGameTimeEvent(0.1f, 0.0f))
        {
            bool needDetection = true;
            if (lastVelocityY <= 0.0f && velocity.posY > 0.0f) // 起跳的话，跳过一次检测
            {
                needDetection = false;
            }
            else if (lastVelocityY < 0.0f || changeY < 0.0f) // 下坠也跳过检测
            {
                needDetection = false;
            }
            else if (velocity.posY > 0.0f && velocity.posY < lastVelocityY)
            {
                needDetection = false;
            }

            if (needDetection)
            {
                Point3 point = position.toPoint3();
                if (componentBody->standingOnBody == nullptr)
                {
                    bool cheat = true;
                    for (int32_t x = -1; x <= 1; x++)
                    {
                        for (int32_t z = -1; z <= 1; z++)
                        {
                            for (int32_t y = -1; y <= 0; y++)
                            {
                                if (terrain->getCellContents(point.posX + x, point.posY + y, point.posZ + z) != AirBlock::Index())
                                {
                                    cheat = false;
                                    break;
                                }
                            }
                        }
                    }
                    if (cheat == true)
                    {
                        if (flyDetectionFactory > 3)
                        {
                            if (flyDetectionFactory % 5 == 0)
                            {
                                Log::Warn(fmt::format("玩家 {} 疑似使用飞行外挂", playerData->username));
                            }
                            addCheatCount(5);
                        }
                        flyDetectionFactory++;
                    }
                    else if (flyDetectionFactory > 0)
                    {
                        flyDetectionFactory--;
                    }
                }
            }
            else if (flyDetectionFactory > 0)
            {
                flyDetectionFactory--;
            }
        }

    END_CHECK:

        if (timeOfDay->periodicGameTimeEvent(0.5f, 0.0f))
        {
            if (isTriggerCheat == true)
            {
                isTriggerCheat = false;
            }
            else if (numberOfAnomalies > 0)
            {
                numberOfAnomalies--;
            }
        }

        if (numberOfAnomalies > 60)
        {
            numberOfAnomalies = 0;
            playerData->componentPlayer->componentHealth->injure(999.0f, entt::null, true, "疑似作弊被幽灵死神带走了", InjureType::Plugin);
        }
    }

    void ComponentAntiCheatBase::load(const nlohmann::json &jsonData)
    {
        timeOfDay = GameSingleton::singletonPtr->getSingleton<SubsystemTimeOfDay>();
        terrain = GameSingleton::singletonPtr->getSingleton<SubsystemTerrain>();

        authPlugin = GameSingleton::pluginsManager->findPlugin<AuthPlugin>();

        componentPlayer = GameSingleton::gameRegistry->get<ComponentPlayer *>(entity);
        allowAnomaliesTime = 5.0f;
        numberOfAnomalies = 0;
        isTriggerCheat = false;
        lastVelocityY = 0.0f;
        flyDetectionFactory = 0;
    }

    static const std::string componentName = "AntiCheat";
    const std::string &ComponentAntiCheatBase::getName() const
    {
        return componentName;
    }
    const std::string &ComponentAntiCheatBase::GetName()
    {
        return componentName;
    }

} // namespace PocketSurvival
